﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using DataTier.Entities;
using DataTier.Helpers;
using System.Data.Objects;

namespace DataTier.Repository
{
    /// <summary>
    /// Репозиторий для работы с Расходными накладными
    /// </summary>
    public class ExpendBillRepository : RepositoryBase
    {
        public ExpendBillRepository()
        {

        }

        public ExpendBillRepository(IDbConnection connection)
            : base(connection)
        {

        }


        /// <summary>
        /// Возвращает список всех Расходных накладных по всем заказам из базы данных
        /// </summary>
        /// <returns></returns>
        public List<ExpendBillEntity> GetExpendBillAllList(int currentUserId, int numPage, int onPage, SearchParameters param = null)
        {
            using (var dbcontext = CreateDataContext())
            {
                IQueryable<ExpendBill> bills = GetExpendBillAllListQueryable(dbcontext, currentUserId, numPage, onPage, param);
                List<ExpendBill> expendBillList = bills.ToList();
                return expendBillList.Select(b => EntityHelper.GetExpendBillEntity(b)).ToList();
            }
        }

        private IQueryable<ExpendBill> GetExpendBillAllListQueryable(DBModelContainer dbcontext, int currentUserId, int numPage, int onPage, SearchParameters param = null)
        {
            IQueryable<ExpendBill> bills = dbcontext.ExpendBill
                .Include("ExpendBillSpec")
                .Include("State")
                .Include("User")
                .Include("Order")
                .Include("Order.User")
                .Include("Order.User.CostsCenter")
                .Include("Order.User.CostsCenter.Department")
                .Include("Order.User.CostsCenter.Department.JuridicalPerson")
                .Include("Order.User.CostsCenter.Department.JuridicalPerson.Company");

            #region Применение фильтров
            if (param != null)
            {
                if (!string.IsNullOrEmpty(param.OrderNumber))
                    bills = bills
                        .Where(b => b.Order.Number.Substring(0, param.OrderNumber.Length).ToLower() == param.OrderNumber.ToLower()); // Начинается со строки

                if (param.OrderCreatedDate.HasValue)
                    bills = bills
                        .Where(b =>
                            b.Order.CreatedDate.Year == param.OrderCreatedDate.Value.Year &&
                            b.Order.CreatedDate.Month == param.OrderCreatedDate.Value.Month &&
                            b.Order.CreatedDate.Day == param.OrderCreatedDate.Value.Day);

                if (!string.IsNullOrEmpty(param.ExpendBillNumberTotal))
                    bills = bills
                        .Where(b => b.NumberTotal.Substring(0, param.ExpendBillNumberTotal.Length).ToLower() == param.ExpendBillNumberTotal.ToLower()); // Начинается со строки

                if (param.ExpendBillDate.HasValue)
                    bills = bills
                        .Where(b =>
                            b.CreatedDate.Year == param.ExpendBillDate.Value.Year &&
                            b.CreatedDate.Month == param.ExpendBillDate.Value.Month &&
                            b.CreatedDate.Day == param.ExpendBillDate.Value.Day);

                if (!string.IsNullOrEmpty(param.ExpendBillStatus))
                    bills = bills
                        .Where(b => b.State.Name.Substring(0, param.ExpendBillStatus.Length).ToLower() == param.ExpendBillStatus.ToLower()); // Начинается со строки

                if (!string.IsNullOrEmpty(param.Department))
                    bills = bills
                        .Where(b => b.Order.User.CostsCenter.Department.Name.ToLower().Contains(param.Department.ToLower())); // Вхождение строки
                //.Where(b => b.Order.User.CostsCenter.Department.Name.Substring(0, param.Department.Length).ToLower() == param.Department.ToLower()); // Начинается со строки

                if (!string.IsNullOrEmpty(param.CostsCentr))
                    bills = bills
                        .Where(b => b.Order.User.CostsCenter.Name.ToLower().Contains(param.CostsCentr.ToLower())); // Вхождение строки
                //.Where(b => b.Order.User.CostsCenter.Name.Substring(0, param.CostsCentr.Length).ToLower() == param.CostsCentr.ToLower()); // Начинается со строки

                if (!string.IsNullOrEmpty(param.JuridicalPerson))
                    bills = bills
                        .Where(b => b.Order.User.CostsCenter.Department.JuridicalPerson.Name.ToLower().Contains(param.JuridicalPerson.ToLower())); // Вхождение строки
                //.Where(b => b.Order.User.CostsCenter.Department.JuridicalPerson.Name.Substring(0, param.JuridicalPerson.Length).ToLower() == param.JuridicalPerson.ToLower()); // Начинается со строки

            }
            #endregion Применение фильтров

            return bills.OrderByDescending(b => b.Id).Skip(onPage * (numPage - 1)).Take(onPage);
        }


        /// <summary>
        /// Возвращает количество Расходных накладных по всем заказам из базы данных
        /// </summary>
        /// <returns></returns>
        public int GetExpendBillAllListCount(int currentUserId, SearchParameters param = null)
        {
            using (var dbcontext = CreateDataContext())
            {
                IQueryable<ExpendBill> bills = GetExpendBillAllListQueryable(dbcontext, currentUserId, 1, 100000000, param);
                return bills.Count();
            }
        }


        /// <summary>
        /// Возвращает список всех Расходных накладных по конкретногму Заказу из базы данных
        /// </summary>
        /// <returns></returns>
        public List<ExpendBillEntity> GetExpendBillList(int currentUserId, bool onlyIts, int orderId)
        {
            using (var dbcontext = CreateDataContext())
            {
                var bills = dbcontext.ExpendBill
                    .Include("ExpendBillSpec")
                    .Include("State")
                    .Include("User")
                    .Where(b => b.OrderId == orderId && b.CreatedUserId == ((onlyIts) ? currentUserId : b.CreatedUserId))
                    .ToList();

                if (bills == null)
                    return null;

                return bills.Select(b => EntityHelper.GetExpendBillEntity(b)).ToList();
            }
        }


        /// <summary>
        /// Возвращает Расходную накладную из базы данных
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public ExpendBillEntity GetExpendBill(int currentUserId, int id)
        {
            using (var dbcontext = CreateDataContext())
            {
                var bill = dbcontext.ExpendBill
                    .Include("ExpendBillSpec")
                    .Include("State")
                    .Include("User")
                    .Where(b => b.Id == id).FirstOrDefault();

                return EntityHelper.GetExpendBillEntity(bill);
            }
        }


        /// <summary>
        /// Формируется новый объект Расходная накладная по конкретному Заказу
        /// </summary>
        /// <param name="currentUserId"></param>
        /// <param name="orderId"></param>
        /// <returns></returns>
        public ExpendBillEntity CreateNewExpendBill(int currentUserId, int orderId)
        {
            var order = new OrdersRepository().GetOrder(currentUserId, orderId);
            int number = this.GetLastNumberExpendBill(orderId);
            if (order != null)
            {
                var bill = new ExpendBillEntity();
                bill.Order = order;
                bill.ExpendBill = new ExpendBill()
                {
                    Number = number+1,
                    NumberTotal = string.Format("{0} {1}", order.Header.AccNumber, (number + 1)),
                    ExpendBillDate = DateTime.Now
                };

                if (order.Specification != null)
                {
                    if (number == 0) // Создается ПЕРВАЯ Расходная накладная для Заказа. В нее вставляются ВСЕ товары из Заказа
                    {
                        int rowid = 0;
                        var listSpec = new List<ExpendBillSpec>();
                        foreach (var spec in order.Specification)
                        {
                            rowid++;
                            var billSpec = new ExpendBillSpec()
                            {
                                ProductId = spec.Specification.ProductId,
                                Artikul = spec.Specification.Artikul,
                                ProductName = spec.Specification.ProductName,
                                Price = spec.Specification.Price,
                                IsUnavailable = spec.Specification.IsUnavailable,
                                SupplierName = spec.Specification.SupplierName,
                                SupplierArtikul = spec.Specification.SupplierArtikul,
                                Count = spec.Specification.Count,
                                Summa = spec.Specification.Summa,
                                RowId = rowid
                            };
                            //billSpec.Product = spec.Product;

                            listSpec.Add(billSpec);
                        }

                        bill.Specification = listSpec;
                    }
                    else // Создается НЕ ПЕРВАЯ Расходная накладная для Заказа. В нее вставляются ОСТАВШИЕСЯ товары из Заказа
                    {
                        var products = new Dictionary<int, int>();
                        using (var dbcontext = CreateDataContext())
                        {
                            var bills = dbcontext.ExpendBill.Where(b => b.OrderId == orderId).ToList();
                            if (bills != null && bills.Count > 0)
                            {
                                Dictionary<int, int> notequalsProducts = ExistNonAddindProduct(bills, order.Specification.Select(s => s.Specification).ToList());

                                if (notequalsProducts == null || notequalsProducts.Count == 0)
                                    throw new Exception("В данном Заказе не осталось неотгруженных товаров!");

                                int rowid = 0;
                                var listSpec = new List<ExpendBillSpec>();
                                foreach (var spec in order.Specification)
                                {
                                    if (notequalsProducts.ContainsKey(spec.Specification.ProductId))
                                    {
                                        rowid++;
                                        var billSpec = new ExpendBillSpec()
                                        {
                                            ProductId = spec.Specification.ProductId,
                                            Artikul = spec.Specification.Artikul,
                                            ProductName = spec.Specification.ProductName,
                                            Price = spec.Specification.Price,
                                            IsUnavailable = spec.Specification.IsUnavailable,
                                            SupplierName = spec.Specification.SupplierName,
                                            SupplierArtikul = spec.Specification.SupplierArtikul,
                                            //Count = spec.Specification.Count,
                                            //Summa = spec.Specification.Summa,
                                            Count = notequalsProducts[spec.Specification.ProductId],
                                            Summa = ((spec.Specification.Price.HasValue) ? spec.Specification.Price.Value : 0.0) * (double)spec.Specification.Count,
                                            RowId = rowid
                                        };
                                        //billSpec.Product = spec.Product;

                                        listSpec.Add(billSpec);

                                        notequalsProducts.Remove(spec.Specification.ProductId);
                                        if (notequalsProducts.Count == 0)
                                            break;
                                    }
                                }

                                bill.Specification = listSpec;
                            }
                        }
                    }
                }
                
                return bill;
            }
            else
            {
                return null;
            }
        }


        /// <summary>
        /// Существуют ли товары Заказа не добавленные в Расходные накладные
        /// </summary>
        /// <returns></returns>
        private Dictionary<int, int> ExistNonAddindProduct(List<ExpendBill> expendedBills, List<OrderSpec> orderSpecs)
        {
            // Спецификация товаров в исходном Заказе
            Dictionary<int, int> oProducts = new Dictionary<int, int>();
            foreach (var ospec in orderSpecs)
            {
                if (oProducts.ContainsKey(ospec.ProductId))
                    oProducts[ospec.ProductId] += ospec.Count;
                else
                    oProducts.Add(ospec.ProductId, ospec.Count);
            }

            // Спецификация товаров в Расходных накладных
            Dictionary<int, int> ebProducts = new Dictionary<int, int>();
            foreach (var bill in expendedBills)
                foreach (var ebSpec in bill.ExpendBillSpec)
                {
                    if (ebProducts.ContainsKey(ebSpec.ProductId)) // Если есть в словаре такой товар, то увеличивается количество в строке с товаром
                        ebProducts[ebSpec.ProductId] += ebSpec.Count;
                    else // Если нет в словаре такого товара, то добавляется товар с количеством
                        ebProducts.Add(ebSpec.ProductId, ebSpec.Count);
                }

            // Вариант 1
            ////var equals = Enumerable.SequenceEqual(ebProducts, oProducts);
            ////var notequals = Enumerable.Except(ebProducts, oProducts);
            //var notequals = oProducts.Except(ebProducts);
            //if (notequals == null)
            //    return null;
            //Dictionary<int, int> notequalsProducts = notequals.ToDictionary(k => k.Key, v => v.Value);

            // Вариант 2
            //Dictionary<int, int> notequalsProducts = new Dictionary<int, int>();
            //foreach (var prod in oProducts)
            //{
            //    if (!ebProducts.ContainsKey(prod.Key))
            //        notequalsProducts.Add(prod.Key, prod.Value);
            //    else if (ebProducts[prod.Key] < prod.Value)
            //        notequalsProducts.Add(prod.Key, prod.Value - ebProducts[prod.Key]);
            //}

            //if (notequalsProducts.Count == 0)
            //    return null;

            // Вариант 3
            Dictionary<int, int> notequalsProducts = new Dictionary<int, int>();
            notequalsProducts = ProductDictionaryHelper.Except(oProducts, ebProducts);

            return notequalsProducts;
        }


        /// <summary>
        /// Возвращает последний номер Расходной накладной для конкретного Заказа
        /// </summary>
        /// <param name="orderId"></param>
        /// <returns></returns>
        public int GetLastNumberExpendBill(int orderId)
        {
            using (var dbcontext = CreateDataContext())
            {
                var bills = dbcontext.ExpendBill.Where(b => b.OrderId == orderId).ToList();
                
                if (bills == null || bills.Count == 0)
                    return 0;
                
                return bills.Select(b => b.Number).Max();
            }
        }


        /// <summary>
        /// Возвращает последний номер ConsolidatedAccount
        /// </summary>
        /// <param name="orderId"></param>
        /// <returns></returns>
        public int GetLastNumberConsolidatedAccount()
        {
            using (var dbcontext = CreateDataContext())
            {
                var accounts = dbcontext.ConsolidatedAccount.ToList();

                if (accounts == null || accounts.Count == 0)
                    return 0;

                return accounts.Select(a => Convert.ToInt32(a.Number)).Max();
            }
        }


        //public static void SetAllModified<T>(T entity, System.Data.Objects.ObjectContext context) 
        //    where T : System.Data.Objects.DataClasses.IEntityWithKey
        //{
        //    var stateEntry = context.ObjectStateManager.GetObjectStateEntry(entity.EntityKey); 
        //    var propertyNameList = stateEntry.CurrentValues.DataRecordInfo.FieldMetadata.Select(pn => pn.FieldType.Name); 
        //    foreach (var propName in propertyNameList) 
        //        stateEntry.SetModifiedProperty(propName);
        //}


        /// <summary>
        /// Сохранение Расходной накладной в базе данных
        /// </summary>
        /// <param name="currentUserId"></param>
        /// <param name="newExpendBill"></param>
        /// <returns></returns>
        public int SaveExpendBill(int currentUserId, ExpendBillEntity newExpendBill)
        {   
            using (var dbcontext = CreateDataContext())
            {
                var order = newExpendBill.Order;
                int expendBillId = 0;
                ExpendBill savedExpendBill = EntityHelper.GetExpendBill(newExpendBill);
                savedExpendBill.ModifiedDate = DateTime.Now;
                savedExpendBill.ModifiedUserId = currentUserId;
                if (savedExpendBill.Id > 0) // Если происходит обновление уже существующей записи о Расходной накладной в БД
                {
                    //var bill = dbcontext.ExpendBill.Where(b => b.OrderId == order.Header.Id).FirstOrDefault();
                    var bill = dbcontext.ExpendBill.Include("ExpendBillSpec").Where(b => b.Id == savedExpendBill.Id).FirstOrDefault();
                    if (bill != null)
                    {
                        var billSpecList = dbcontext.ExpendBillSpec.Where(s => s.ExpendBillId == savedExpendBill.Id).ToList();

                        savedExpendBill.EntityKey = bill.EntityKey;
                        dbcontext.ExpendBill.ApplyCurrentValues(savedExpendBill);


                        foreach (var spec in billSpecList)
                            dbcontext.ExpendBillSpec.DeleteObject(spec);
                        dbcontext.SaveChanges();

                        int rowid = 0;
                        foreach (var spec in savedExpendBill.ExpendBillSpec)
                        {
                            rowid++;
                            var expendBillSpec = new ExpendBillSpec()
                                                    {
                                                        //ExpendBill = savedExpendBill,
                                                        ExpendBillId = savedExpendBill.Id,
                                                        Count = spec.Count,
                                                        ProductId = spec.ProductId,
                                                        Artikul = spec.Artikul,
                                                        ProductName = spec.ProductName,
                                                        Price = spec.Price,
                                                        IsUnavailable = spec.IsUnavailable,
                                                        SupplierName = spec.SupplierName,
                                                        SupplierArtikul = spec.SupplierArtikul,
                                                        Summa = spec.Summa,
                                                        RowId = rowid
                                                    };
                            dbcontext.ExpendBillSpec.AddObject(expendBillSpec);
                        }
                    }
                }
                else // Если Расходная накладная создается
                {
                    // Создается номер для нового документа
                    int number = this.GetLastNumberExpendBill(order.Header.Id);
                    savedExpendBill.Number = number+1;
                    savedExpendBill.NumberTotal = string.Format("{0} {1}", order.Header.AccNumber, (number + 1));
                    savedExpendBill.CreatedDate = DateTime.Now;
                    savedExpendBill.CreatedUserId = currentUserId;
                    savedExpendBill.StateId = (int)StateEnum.ExpendBillSaved;
                    savedExpendBill.OrderId = order.Header.Id;

                    dbcontext.AddToExpendBill(savedExpendBill);
                }

                dbcontext.SaveChanges();

                expendBillId = savedExpendBill.Id;

                return expendBillId;
            }
        }



        /// <summary>
        /// Удаляются записи из Накладной
        /// </summary>
        /// <param name="currentUserId"></param>
        /// <param name="newExpendBillId"></param>
        /// <param name="productIDs"></param>
        public void RemoveProductsFromExpendBill(int currentUserId, int newExpendBillId, int[] productIDs)
        {
            using (var dbcontext = CreateDataContext())
            {
                var expendBill = dbcontext.ExpendBill.Include("ExpendBillSpec").Where(b => b.Id == newExpendBillId).FirstOrDefault();
                if (expendBill != null)
                {
                    List<ExpendBillSpec> forRemodeSpecs = new List<ExpendBillSpec>();

                    // Формирование списка с объектами-строками спецификации Накладной
                    foreach (var spec in expendBill.ExpendBillSpec)
                        if (productIDs.Contains(spec.ProductId))
                            forRemodeSpecs.Add(spec);
                    
                    // Если список строк для удаления больше нуля
                    if (forRemodeSpecs.Count > 0)
                    {
                        // Удаляются строки с товарами из Накладной
                        foreach (var spec in forRemodeSpecs)
                            expendBill.ExpendBillSpec.Remove(spec);

                        if (expendBill.ExpendBillSpec.Count <= 0) // Удалились все строки в Накладной. Удаляется и сама накладная
                        {
                            dbcontext.ExpendBill.DeleteObject(expendBill);
                        }
                        else // Пересчитываются номера строк
                        {
                            //int rowId = 0;
                            //foreach (var spec in expendBill.ExpendBillSpec)
                            //{
                            //    rowId++;
                            //    if (spec.RowId != rowId)
                            //        spec.RowId = rowId;
                            //}
                            expendBill.ItogoSumm = expendBill.ExpendBillSpec.Sum(s => s.Summa); // Пересчитывается итоговая сумма Накладной
                        }
                        dbcontext.SaveChanges();
                    }
                }
            }
        }



        /// <summary>
        /// Обновление количества товаров в указанных Накладных 
        /// </summary>
        /// <param name="currentUserId"></param>
        /// <param name="billsForModif"></param>
        public void UpdateProductsFromExpendBills(int currentUserId, List<BillProductCountComposite> billsForModif)
        {
            using (var dbcontext = CreateDataContext())
            {
                foreach (var bill in billsForModif)
                {
                    var expendBill = dbcontext.ExpendBill.Include("ExpendBillSpec").Where(b => b.Id == bill.Id).FirstOrDefault();
                    if (expendBill != null)
                    {
                        foreach (var prod in bill.Products)
                        {
                            var spec = expendBill.ExpendBillSpec.Where(s => s.ProductId == prod.Id).FirstOrDefault();
                            if (spec != null)
                            {
                                if (prod.Count == 0)
                                {
                                    expendBill.ExpendBillSpec.Remove(spec); // Если нужно проставить количество товара = 0, то просто удаляем строку с товаром из накладной
                                }
                                else
                                {
                                    spec.Count = prod.Count;                                                        // Обновляется количество товара
                                    spec.Summa = ((spec.Price.HasValue) ? spec.Price.Value : 0.0) * spec.Count;     //  и сумма по строке спецификации в накладной
                                }
                            }
                        }

                        if (expendBill.ExpendBillSpec.Count <= 0) // Удалились все строки в Накладной. Удаляется и сама накладная
                        {
                            dbcontext.ExpendBill.DeleteObject(expendBill);
                        }
                        else // Пересчитываются номера строк
                        {
                            //int rowId = 0;
                            //foreach (var spec in expendBill.ExpendBillSpec)
                            //{
                            //    rowId++;
                            //    if (spec.RowId != rowId)
                            //        spec.RowId = rowId;
                            //}
                            expendBill.ItogoSumm = expendBill.ExpendBillSpec.Sum(s => s.Summa); // Обновляется итоговая сумма накладной
                        }
                    }
                }
                dbcontext.SaveChanges();
            }
        }




        /// <summary>
        /// Удаление Расходной накладной
        /// </summary>
        /// <param name="currentUserId"></param>
        /// <param name="expendBillId"></param>
        public void DeleteExpendBill(int currentUserId, int expendBillId)
        {
            using (var dbcontext = CreateDataContext())
            {
                var bill = dbcontext.ExpendBill.Include("ExpendBillSpec").Where(b => b.Id == expendBillId).FirstOrDefault();
                if (bill != null)
                {
                    dbcontext.ExpendBill.DeleteObject(bill);
                    dbcontext.SaveChanges();
                }
            }
        }


        /// <summary>
        /// Обновление товаров в спецификации Накладной и связанного с ней Заказа
        /// </summary>
        /// <param name="currentUserId"></param>
        /// <param name="orderId"></param>
        public void RefreshExpendBillSpecAndOrderSpec(int currentUserId, int expendBillId)
        {
            using (var dbcontext = CreateDataContext())
            {
                if (expendBillId == 0)
                    return;

                var expendBill = dbcontext.ExpendBill.Include("ExpendBillSpec").Include("ExpendBillSpec.Product").Where(b => b.Id == expendBillId).FirstOrDefault();
                if (expendBill != null)
                {
                    // Обновляется спецификация Накладной
                    foreach (var spec in expendBill.ExpendBillSpec)
                    {
                        spec.Artikul = spec.Product.Artikul;
                        spec.IsUnavailable = spec.Product.IsUnavailable;
                        spec.Price = spec.Product.Price;
                        spec.ProductName = spec.Product.ProductName;
                        spec.SupplierArtikul = spec.Product.SupplierArtikul;
                        spec.SupplierName = spec.Product.SupplierName;
                        spec.Summa = ((spec.Price.HasValue) ? spec.Price.Value : 0.0) * spec.Count;
                    }
                    expendBill.ItogoSumm = expendBill.ExpendBillSpec.Sum(s => s.Summa);


                    // Обновляется спецификция Заказа
                    var order = dbcontext.Order.Include("OrderSpec").Include("OrderSpec.Product").Where(o => o.Id == expendBill.OrderId).FirstOrDefault();
                    if (order != null)
                    {
                        var bills = dbcontext.ExpendBill.Include("ExpendBillSpec").Where(b => b.OrderId == expendBill.OrderId && b.StateId == (int)StateEnum.ExpendBillExecuted).ToList();

                        foreach (var spec in order.OrderSpec)
                        {
                            // В спецификации Заказа обновляются те товары, которые не участвуют в Исполненных накладных
                            if (expendBill.ExpendBillSpec.Where(s => s.ProductId == spec.ProductId).Any() ||  // Если товар присутствует в обновляемой накладной ИЛИ
                                bills == null || // не существует исполненных Накладных к Заказу ИЛИ
                                !bills.Where(b => b.ExpendBillSpec.Where(s => s.ProductId == spec.ProductId).Any()).Any()) // не существуют исполненные накладные, в которых есть обновляемый товар
                            {
                                spec.Artikul = spec.Product.Artikul;
                                spec.IsUnavailable = spec.Product.IsUnavailable;
                                spec.Price = spec.Product.Price;
                                spec.ProductName = spec.Product.ProductName;
                                spec.SupplierArtikul = spec.Product.SupplierArtikul;
                                spec.SupplierName = spec.Product.SupplierName;
                                spec.Summa = ((spec.Price.HasValue) ? spec.Price.Value : 0.0) * (double)spec.Count;
                            }
                        }
                        order.ItogoSumm = order.OrderSpec.Sum(s => s.Summa);
                    }

                    dbcontext.SaveChanges();
                }
            }
        }


        /// <summary>
        /// Формирует новый консолидированный счет
        /// </summary>
        /// <param name="currentUserId"></param>
        /// <param name="costCenterId"></param>
        /// <param name="dateBegin"></param>
        /// <param name="dateEnd"></param>
        /// <returns></returns>
        public ConsolidatedAccountEntity CreateConsolidatedAccount(int currentUserId, int juridicalPersonId, DateTime dateBegin, DateTime dateEnd)
        {
            int lastNumber = GetLastNumberConsolidatedAccount();

            using (var dbcontext = CreateDataContext())
            {
                DateTime tmpdateEnd = dateEnd.AddDays(1);
                var bills = dbcontext.ExpendBill
                    .Include("ExpendBillSpec")
                    .Include("State")
                    .Include("Order")
                    .Include("Order.User")
                    .Where(b => b.Order.User.CostsCenter.Department.JuridicalPersonId == juridicalPersonId && 
                                b.StateId == (int)StateEnum.ExpendBillExecuted &&
                                b.ExpendBillDate.Value >= dateBegin && //b.ModifiedDate.Value >= dateBegin &&
                                b.ExpendBillDate.Value < tmpdateEnd) //b.ModifiedDate.Value < tmpdateEnd)
                    .ToList();

                if (bills == null)
                    return null;

                var account = new ConsolidatedAccount()
                {
                    JuridicalPersonId = juridicalPersonId,
                    PeriodDateStart = dateBegin,
                    PeriodDateEnd = dateEnd,
                    CreatedUserId = currentUserId,
                    CreatedDate = DateTime.Now,
                    Number = (lastNumber+1).ToString()
                };

                account.ConsolidatedAccountSpec = new System.Data.Objects.DataClasses.EntityCollection<ConsolidatedAccountSpec>();
                int rowId = 0;
                account.ExpendBill = new System.Data.Objects.DataClasses.EntityCollection<ExpendBill>();
                foreach (var bill in bills)
                {
                    foreach (var spec in bill.ExpendBillSpec)
                    {
                        var extsted = account.ConsolidatedAccountSpec.Where(s => s.ProductId == spec.ProductId).FirstOrDefault();
                        if (extsted != null) // Такой товар уже существует в спецификации. Просто увеличиваем количество и сумму в нем
                        {
                            extsted.Count += spec.Count;
                            extsted.Summa += spec.Summa;
                        }
                        else // Такого товара не существует в спецификации. Добавляем новую строку с ним
                        {
                            rowId++;
                            ConsolidatedAccountSpec aSpec = new ConsolidatedAccountSpec()
                            {
                                Count = spec.Count,
                                ProductId = spec.ProductId,
                                Artikul = spec.Artikul,
                                ProductName = spec.ProductName,
                                Price = spec.Price,
                                IsUnavailable = spec.IsUnavailable,
                                SupplierName = spec.SupplierName,
                                SupplierArtikul = spec.SupplierArtikul,
                                //Product = spec.Product,
                                Summa = spec.Summa,
                                RowId = rowId
                            };
                            account.ConsolidatedAccountSpec.Add(aSpec);

                        }
                    }
                    account.ExpendBill.Add(bill);
                }

                return EntityHelper.GetConsolidatedAccountEntity(account);
            }
        }


        /// <summary>
        /// Возвращает объект Консолидированный Счет
        /// </summary>
        /// <param name="currentUserId"></param>
        /// <param name="accountId"></param>
        /// <returns></returns>
        public ConsolidatedAccountEntity GetConsolidatedAccount(int currentUserId, int accountId)
        {
            using (var dbcontext = CreateDataContext())
            {
                var account = dbcontext.ConsolidatedAccount
                    .Include("ConsolidatedAccountSpec")
                    .Include("ConsolidatedAccountSpec.Product")
                    .Include("ConsolidatedAccountSpec.Product.MeasureUnit")
                    .Include("State")
                    .Include("User")
                    .Where(c => c.Id == accountId).FirstOrDefault();

                return EntityHelper.GetConsolidatedAccountEntity(account);
            }
        }


        /// <summary>
        /// Сохранение Консолидированного счета в базе данных
        /// </summary>
        /// <param name="currentUserId"></param>
        /// <param name="newConsolidatedAccount"></param>
        /// <returns></returns>
        public int SaveConsolidatedAccount(int currentUserId, ConsolidatedAccountEntity newConsolidatedAccount)
        {
            using (var dbcontext = CreateDataContext())
            {
                int accountId = 0;
                ConsolidatedAccount savedConsolidatedAccount = EntityHelper.GetConsolidatedAccount(newConsolidatedAccount);
                if (savedConsolidatedAccount.Id > 0) // Если происходит обновление уже существующей записи о Консолидированном счете в БД
                {
                    dbcontext.ConsolidatedAccount.Attach(savedConsolidatedAccount);
                    dbcontext.ObjectStateManager.ChangeObjectState(savedConsolidatedAccount, EntityState.Modified);
                    foreach (var acs in savedConsolidatedAccount.ConsolidatedAccountSpec)
                        dbcontext.ObjectStateManager.ChangeObjectState(acs, EntityState.Added);

                    // Удаляется предварительно спецификация Консолидированного счета
                    var specs = dbcontext.ConsolidatedAccountSpec.Where(s => s.ConsolidatedAccountId == newConsolidatedAccount.ConsolidatedAccount.Id).ToList();
                    if (specs != null)
                        foreach (var spec in specs)
                            dbcontext.ConsolidatedAccountSpec.DeleteObject(spec);
                }
                else // Если Консолидированный счет создается
                {
                    savedConsolidatedAccount.CreatedDate = DateTime.Now;
                    savedConsolidatedAccount.CreatedUserId = currentUserId;
                    savedConsolidatedAccount.StateId = (int)StateEnum.ConsolidatedAccountSaved;

                    dbcontext.AddToConsolidatedAccount(savedConsolidatedAccount);

                    // Встаить в коллекцию Расходных накладных Id Консолидированного счета
                    var expendBills = dbcontext.ExpendBill.Where(b => newConsolidatedAccount.ExpendBillIds.Contains(b.Id)).ToList();
                    if (expendBills != null)
                        foreach (var expendBill in expendBills)
                            expendBill.ConsolidatedAccount = savedConsolidatedAccount;
                }

                dbcontext.SaveChanges();

                accountId = savedConsolidatedAccount.Id;

                return accountId;
            }
        }


        /// <summary>
        /// Возвращает список Консолидированных счетов из базы данных
        /// </summary>
        /// <returns></returns>
        public List<ConsolidatedAccountEntity> GetConsolidatedAccountList(int currentUserId, int numPage, int onPage, SearchParameters param = null)
        {
            using (var dbcontext = CreateDataContext())
            {
                IQueryable<ConsolidatedAccount> accounts = GetConsolidatedAccountListQueryable(dbcontext, currentUserId, numPage, onPage, param);
                List<ConsolidatedAccount> accountList = accounts.ToList();
                return accountList.Select(a => EntityHelper.GetConsolidatedAccountEntity(a)).ToList();
            }
        }

        private IQueryable<ConsolidatedAccount> GetConsolidatedAccountListQueryable(DBModelContainer dbcontext, int currentUserId, int numPage, int onPage, SearchParameters param = null)
        {
            IQueryable<ConsolidatedAccount> accounts = dbcontext.ConsolidatedAccount
                .Include("ConsolidatedAccountSpec")
                .Include("ConsolidatedAccountSpec.Product")
                .Include("ConsolidatedAccountSpec.Product.MeasureUnit")
                .Include("State")
                .Include("User")
                .Include("JuridicalPerson");

            #region Применение фильтров
            if (param != null)
            {
                if (!string.IsNullOrEmpty(param.AccountNumber))
                    accounts = accounts
                        .Where(c => !string.IsNullOrEmpty(c.Number) && c.Number.Substring(0, param.AccountNumber.Length).ToLower() == param.AccountNumber.ToLower()); // Начинается со строки

                if (param.AccountCreatedDate.HasValue)
                    accounts = accounts
                        .Where(c =>
                            c.CreatedDate.Year == param.AccountCreatedDate.Value.Year &&
                            c.CreatedDate.Month == param.AccountCreatedDate.Value.Month &&
                            c.CreatedDate.Day == param.AccountCreatedDate.Value.Day);

                if (param.AccountPeriodDateStart.HasValue)
                    accounts = accounts
                        .Where(c =>
                            c.PeriodDateStart.Year == param.AccountPeriodDateStart.Value.Year &&
                            c.PeriodDateStart.Month == param.AccountPeriodDateStart.Value.Month &&
                            c.PeriodDateStart.Day == param.AccountPeriodDateStart.Value.Day);

                if (param.AccountPeriodDateEnd.HasValue)
                    accounts = accounts
                        .Where(c =>
                            c.PeriodDateEnd.Year == param.AccountPeriodDateEnd.Value.Year &&
                            c.PeriodDateEnd.Month == param.AccountPeriodDateEnd.Value.Month &&
                            c.PeriodDateEnd.Day == param.AccountPeriodDateEnd.Value.Day);

                if (!string.IsNullOrEmpty(param.JuridicalPerson))
                    accounts = accounts
                        .Where(c => c.JuridicalPerson.Name.ToLower().Contains(param.JuridicalPerson.ToLower())); // Вхождение строки
                //.Where(c => c.JuridicalPerson.Name.Substring(0, param.JuridicalPerson.Length).ToLower() == param.JuridicalPerson.ToLower()); // Начинается со строки
            }
            #endregion Применение фильтров

            return accounts.OrderByDescending(b => b.Id).Skip(onPage * (numPage - 1)).Take(onPage);
        }

        /// <summary>
        /// Возвращает количество Консолидированных счетов из базы данных
        /// </summary>
        /// <returns></returns>
        public int GetConsolidatedAccountListCount(int currentUserId, SearchParameters param = null)
        {
            using (var dbcontext = CreateDataContext())
            {
                IQueryable<ConsolidatedAccount> list = GetConsolidatedAccountListQueryable(dbcontext, currentUserId, 1, 100000000, param);
                return list.Count();
            }
        }







        /// <summary>
        /// Формирует новый консолидированный счет
        /// </summary>
        /// <param name="currentUserId"></param>
        /// <param name="costCenterId"></param>
        /// <param name="dateBegin"></param>
        /// <param name="dateEnd"></param>
        /// <returns></returns>
        public CAEntity2 CreateConsolidatedAccount2(int currentUserId, int juridicalPersonId, DateTime dateBegin, DateTime dateEnd)
        {
            int lastNumber = GetLastNumberConsolidatedAccount();

            using (var dbcontext = CreateDataContext())
            {
                DateTime tmpdateEnd = dateEnd.AddDays(1);
                var bills = dbcontext.ExpendBill
                    .Include("ExpendBillSpec")
                    .Include("State")
                    .Include("Order")
                    .Include("Order.User")
                    .Where(b => b.Order.User.CostsCenter.Department.JuridicalPersonId == juridicalPersonId &&
                                b.StateId == (int)StateEnum.ExpendBillExecuted &&
                                b.ExpendBillDate.Value >= dateBegin && //b.ModifiedDate.Value >= dateBegin &&
                                b.ExpendBillDate.Value < tmpdateEnd) //b.ModifiedDate.Value < tmpdateEnd)
                    .ToList();

                if (bills == null)
                    return null;
                
                var account = new ConsolidatedAccount()
                {
                    JuridicalPersonId = juridicalPersonId,
                    PeriodDateStart = dateBegin,
                    PeriodDateEnd = dateEnd,
                    CreatedUserId = currentUserId,
                    CreatedDate = DateTime.Now,
                    Number = (lastNumber + 1).ToString()
                };

                account.ConsolidatedAccountSpec = new System.Data.Objects.DataClasses.EntityCollection<ConsolidatedAccountSpec>();
                int rowId = 0;
                account.ExpendBill = new System.Data.Objects.DataClasses.EntityCollection<ExpendBill>();
                foreach (var bill in bills)
                {
                    foreach (var spec in bill.ExpendBillSpec)
                    {
                        var extsted = account.ConsolidatedAccountSpec.Where(s => s.ProductId == spec.ProductId && Math.Round(s.Price.Value, 2) == Math.Round(spec.Price.Value, 2)).FirstOrDefault();
                        if (extsted != null) // Такой товар уже существует в спецификации. Просто увеличиваем количество и сумму в нем
                        {
                            extsted.Count += spec.Count;
                            extsted.Summa += spec.Summa;
                        }
                        else // Такого товара не существует в спецификации. Добавляем новую строку с ним
                        {
                            rowId++;
                            ConsolidatedAccountSpec aSpec = new ConsolidatedAccountSpec()
                            {
                                Count = spec.Count,
                                ProductId = spec.ProductId,
                                Artikul = spec.Artikul,
                                ProductName = spec.ProductName,
                                Price = spec.Price,
                                IsUnavailable = spec.IsUnavailable,
                                SupplierName = spec.SupplierName,
                                SupplierArtikul = spec.SupplierArtikul,
                                //Product = spec.Product,
                                Summa = spec.Summa,
                                RowId = rowId
                            };
                            account.ConsolidatedAccountSpec.Add(aSpec);

                        }
                    }
                    account.ExpendBill.Add(bill);
                }

                return EntityHelper.GetConsolidatedAccountEntity2(account);
            }
        }


        /// <summary>
        /// Возвращает объект Консолидированный Счет
        /// </summary>
        /// <param name="currentUserId"></param>
        /// <param name="accountId"></param>
        /// <returns></returns>
        public CAEntity2 GetConsolidatedAccount2(int currentUserId, int accountId)
        {
            using (var dbcontext = CreateDataContext())
            {
                var account = dbcontext.ConsolidatedAccount
                    .Include("ConsolidatedAccountSpec")
                    .Include("ConsolidatedAccountSpec.Product")
                    .Include("ConsolidatedAccountSpec.Product.MeasureUnit")
                    .Include("State")
                    .Include("User")
                    .Where(c => c.Id == accountId).FirstOrDefault();

                return EntityHelper.GetConsolidatedAccountEntity2(account);
            }
        }


        /// <summary>
        /// Сохранение Консолидированного счета в базе данных
        /// </summary>
        /// <param name="currentUserId"></param>
        /// <param name="newConsolidatedAccount"></param>
        /// <returns></returns>
        public int SaveConsolidatedAccount2(int currentUserId, CAEntity2 newConsolidatedAccount)
        {
            using (var dbcontext = CreateDataContext())
            {
                int accountId = 0;
                ConsolidatedAccount savedConsolidatedAccount = EntityHelper.GetConsolidatedAccount2(newConsolidatedAccount);
                if (savedConsolidatedAccount.Id > 0) // Если происходит обновление уже существующей записи о Консолидированном счете в БД
                {
                    dbcontext.ConsolidatedAccount.Attach(savedConsolidatedAccount);
                    dbcontext.ObjectStateManager.ChangeObjectState(savedConsolidatedAccount, EntityState.Modified);
                    foreach (var acs in savedConsolidatedAccount.ConsolidatedAccountSpec)
                        dbcontext.ObjectStateManager.ChangeObjectState(acs, EntityState.Added);

                    // Удаляется предварительно спецификация Консолидированного счета
                    var specs = dbcontext.ConsolidatedAccountSpec.Where(s => s.ConsolidatedAccountId == newConsolidatedAccount.Id).ToList();
                    if (specs != null)
                        foreach (var spec in specs)
                            dbcontext.ConsolidatedAccountSpec.DeleteObject(spec);
                }
                else // Если Консолидированный счет создается
                {
                    savedConsolidatedAccount.CreatedDate = DateTime.Now;
                    savedConsolidatedAccount.CreatedUserId = currentUserId;
                    savedConsolidatedAccount.StateId = (int)StateEnum.ConsolidatedAccountSaved;

                    dbcontext.AddToConsolidatedAccount(savedConsolidatedAccount);

                    // Встаить в коллекцию Расходных накладных Id Консолидированного счета
                    var expendBills = dbcontext.ExpendBill.Where(b => newConsolidatedAccount.ExpendBillIds.Contains(b.Id)).ToList();
                    if (expendBills != null)
                        foreach (var expendBill in expendBills)
                            expendBill.ConsolidatedAccount = savedConsolidatedAccount;
                }

                dbcontext.SaveChanges();

                accountId = savedConsolidatedAccount.Id;

                return accountId;
            }
        }


        /// <summary>
        /// Возвращает список Консолидированных счетов из базы данных
        /// </summary>
        /// <returns></returns>
        public List<CAEntity2> GetConsolidatedAccountList2(int currentUserId, int numPage, int onPage, SearchParameters param = null)
        {
            using (var dbcontext = CreateDataContext())
            {
                IQueryable<ConsolidatedAccount> accounts = GetConsolidatedAccountListQueryable(dbcontext, currentUserId, numPage, onPage, param);
                List<ConsolidatedAccount> accountList = accounts.ToList();
                return accountList.Select(a => EntityHelper.GetConsolidatedAccountEntity2(a)).ToList();
            }
        }

        

        /// <summary>
        /// Возвращает количество Консолидированных счетов из базы данных
        /// </summary>
        /// <returns></returns>
        public int GetConsolidatedAccountListCount2(int currentUserId, SearchParameters param = null)
        {
            return GetConsolidatedAccountListCount(currentUserId, param);
        }


        /// <summary>
        /// Возвращает объединенный объект для экспортной формы Отгрузка (на основании Консолидированного счета)
        /// </summary>
        /// <param name="accountId"></param>
        /// <returns></returns>
        public ConsolidatedAccountOtgruzkaEntity GetConsolidatedAccountOtgruzka(int accountId)
        {
            using (var dbcontext = CreateDataContext())
            {
                var account = dbcontext.ConsolidatedAccount
                    .Include("ConsolidatedAccountSpec")
                    .Include("ConsolidatedAccountSpec.Product")
                    .Include("ConsolidatedAccountSpec.Product.MeasureUnit")
                    .Include("ExpendBill")
                    .Include("ExpendBill.Order")
                    .Include("State")
                    .Include("User")
                    .Include("User.CostsCenter")
                    .Where(c => c.Id == accountId).FirstOrDefault();

                return EntityHelper.GetConsolidatedAccountOtgruzkaEntity(account);
            }
        }


        /// <summary>
        /// Возвращает объединенный объект для экспортной формы Накладная (на основании Консолидированного счета)
        /// </summary>
        /// <param name="accountId"></param>
        /// <returns></returns>
        public ConsolidatedAccountInvoiceEntity GetConsolidatedAccountInvoice(int accountId)
        {
            using (var dbcontext = CreateDataContext())
            {
                var account = dbcontext.ConsolidatedAccount
                    .Include("ConsolidatedAccountSpec")
                    .Include("ConsolidatedAccountSpec.Product")
                    .Include("ConsolidatedAccountSpec.Product.MeasureUnit")
                    .Include("ExpendBill")
                    .Include("ExpendBill.Order")
                    .Include("State")
                    .Include("User")
                    .Include("User.CostsCenter")
                    .Where(c => c.Id == accountId).FirstOrDefault();

                return EntityHelper.GetConsolidatedAccountInvoiceEntity(account);
            }
        }


    }
}
